class: title-slide, middle, hide-count ### Por fin VieRnes | 11 de Agosto 2023 # Haciendo mapas con R <br> ## Dr. Agustín Alesso <br> <br> ### ??? --- ## Outline - ¿Por qué hacer mapas? ¿por qué con R? - Evolución datos espaciales en R - Mapas estáticos vs interactivos - Paquetes para mapear -- <br> .small[ Código reproducible instalando estos paquetes. ```r # install.packages("pacman") # En el caso que no lo tengan instalado pacman::p_install_gh("AgRoMeteorologiaINTA/agromet") pacman::p_load( tidyverse, gridExtra, patchwork, # manejo de datos y ploteo sp, sf, # clases objetos espaciales ggspatial, tmap, leaflet, mapview, # para visualizar agromet # datos de INTA ) ``` O, probar el código siguiendo este
R-notebook
] --- name: why ## ¿Por qué hacer mapas? --- template: why .pull-left[ <div class="figure" style="text-align: center"> <img src="data:image/png;base64,#https://upload.wikimedia.org/wikipedia/commons/c/c7/Snow-cholera-map.jpg" alt="Mapa de Cólera Londers 1854" width="80%" /> <p class="caption">Mapa de Cólera Londers 1854</p> </div> ] ??? Maps have been used for several thousand years for a wide variety of purposes. Historic examples include maps of buildings and land ownership in the Old Babylonian dynasty more than 3000 years ago and Ptolemy’s world map in his masterpiece Geography nearly 2000 years ago En uno de los primeros análisis de datos espaciales el Dr. Snow mapeó los casos de cólera en el barrio Soho de Londres y la disposición de las estaciones de bombeo. De la visualización de ambas capas de información descubró que el pozo contaminado se encontraba en la calle Broad. -- .pull-right[ - Ideal para visualizar, explorar o analizar datos espaciales - Permiten ver tendencias, patrones, detectar anomalías, outliers, etc. - Modo efectivo para comunicar resultados de análisis de datos espaciales {{content}} ] -- **¿Por que en R?** {{content}} -- reproducibilidad! {{content}} -- <br>
**Cuidado**: Mapas mal preparados pueden dificultar la comunicación. - mala ubicación de los componentes - tamaño y legibilidad del texto - falta de referencias/leyenda - mala elección de colores y simbología
Acá una guía de estilo de [Journal of Maps](https://files.taylorandfrancis.com/TJOM-suppmaterial-quick-guide.pdf) ??? Los mapas permien ver tendencias, patrones, valores extraños, etc Ejemplo de tendencia Ejemplo clásico pozos y colera --- background-image: url(data:image/png;base64,#https://camo.githubusercontent.com/63d5460e5fdfb78d42eedeaa54c14b63fcb7c872ac85e8c49fd1fd827553e9be/68747470733a2f2f6d74656e6e656b65732e6769746875622e696f2f646f776e6c6f6164732f696d616765732f636c61737369632e706e67) background-size: contain background-position: center --- background-image: url(data:image/png;base64,#https://camo.githubusercontent.com/4fccc53ab6c73754e5cc8649fd89508e7033c6a5f1a239e524e8451fde6cbc05/68747470733a2f2f6d74656e6e656b65732e6769746875622e696f2f646f776e6c6f6164732f696d616765732f555363686f726f2e706e67) background-size: contain background-position: center --- background-image: url(data:image/png;base64,#https://camo.githubusercontent.com/8143ec3e62de5d7e8b6fb9483a3e281e3c2a790f209e26bc12ba4668ccf7fb4a/68747470733a2f2f6d74656e6e656b65732e6769746875622e696f2f646f776e6c6f6164732f696d616765732f776f726c645f666163657473322e706e67) background-size: contain background-position: center --- name: spatial background-image: url(data:image/png;base64,#https://r-spatial.org/images/logo.png) background-position: 95% 5% background-size: 10% ## Datos espaciales en R: evolución .footnote[ Fuente: [R-Bloggers](https://www.r-bloggers.com/2023/06/upcoming-changes-to-popular-r-packages-for-spatial-data-what-you-need-to-do/), [Bivand et al. (2021)](https://link.springer.com/article/10.1007/s10109-020-00336-0#Sec2) ] --- template: spatial .pull-left[
] .pull-right[ Proveedor de las clases `Spatial*` y `Spatial*DataFrame` y sus métodos ```r data("meuse") coordinates(meuse) <- ~x+y proj4string(meuse) <- CRS("+init=epsg:28992") meuse ``` ``` class : SpatialPointsDataFrame features : 155 extent : 178605, 181390, 329714, 333611 (xmin, xmax, ymin, ymax) crs : +proj=sterea +lat_0=52.1561605555556 +lon_0=5.38763888888889 +k=0.9999079 +x_0=155000 +y_0=463000 +ellps=bessel +units=m +no_defs variables : 12 names : cadmium, copper, lead, zinc, elev, dist, om, ffreq, soil, lime, landuse, dist.m min values : 0.2, 14, 37, 113, 5.18, 0, 1, 1, 1, 0, Aa, 10 max values : 18.1, 128, 654, 1839, 10.52, 0.880389, 17, 3, 3, 1, W, 1000 ``` Slots para: `bbox`, `coords`, `proj4string` y `data` (los que terminan en `DataFrame`) ] --- template: spatial .pull-left[
] .pull-right[ - `maptools` da utilidades varias para manejar vectores ] --- template: spatial .pull-left[
] .pull-right[ - `maptools` da utilidades varias para manejar vectores - `rgdal` para leer/guardar datos espaciales - `rgeos` para operar vectores - `raster` para operar rasters ] --- template: spatial .pull-left[
] .pull-right[ - `maptools` da utilidades varias para manejar vectores - `rgdal` para leer/guardar datos espaciales - `rgeos` para operar vectores - `raster` para operar rasters - `spacetime` aporta clases y métodos para datos espacio-temporales ] --- template: spatial .pull-left[
] .pull-right[ Nueva generación... - `maptools/rgeos/rgdal` se retiran en Oct 2023 - `sf` (simple feature) evolución de `sp`, combina GEOS y GDAL `tidyverse`-friendly ] --- template: spatial .pull-left[
] .pull-right[ Nueva generación... - `maptools/rgeos/rgdal` se retiran en Oct 2023 - `sf` (simple feature) evolución de `sp`, combina GEOS y GDAL `tidyverse`-friendly - `stars` vectores y rasters espaciotemporales ] --- template: spatial .pull-left[
] .pull-right[ Nueva generación... - `maptools/rgeos/rgdal` se retiran en Oct 2023 - `sf` (simple feature) evolución de `sp`, combina GEOS y GDAL `tidyverse`-friendly - `stars` vectores y rasters espaciotemporales - `terra` evolución de `raster` ] --- name: estatico-interact ## Mapas estáticos vs interactivos --- template: estatico-interact <img src="data:image/png;base64,#mapas_con_R_files/figure-html/unnamed-chunk-13-1.png" width="864" style="display: block; margin: auto;" /> --- template: estatico-interact
--- ## Paquetes para mapear .pull-left[ Se pueden hacer "mapas" usando funciones básicas (y muuuucho código) o bien con paquetes dedicados: - `sp` a través de `spplot()` - `sf` usando `plot()` - `raster` - `ggplot2` usando `geom_sf()` (+ `ggspatial`) - `tmap` - `mapview` - `leaflet` - `rbokeh`, un port the paquete `bokeh` de Python - ... ] -- .pull-right[ <img src="data:image/png;base64,#mapas_con_R_files/figure-html/unnamed-chunk-15-1.png" width="504" style="display: block; margin: auto;" /> ] --- name: spplot ## `sp` --- template: spplot .pull-left[ - `sp::plot()` usa plot base para objetos espaciales - `sp::spplot()` usa el paquete `grid` (similar a `lattice`) - Soportan vectores y raster como objetos `Spatial*` - Son rápidos para explorar - Sintaxis compleja para composiciones avanzadas - `sp::spplot()` gráficos multipanel ] -- .pull-right[ Preparando unos datos para probar... ```r # Datos punto data("meuse") coordinates(meuse) <- ~x+y proj4string(meuse) <- CRS("+init=epsg:28992") # Datos raster data("meuse.grid") coordinates(meuse.grid) = ~x+y proj4string(meuse.grid) <- CRS("+init=epsg:28992") gridded(meuse.grid) = TRUE ``` ] --- template: spplot .pull-left[ Algo básico con `sp::plot()`... ```r labs <- cut(meuse$zinc, 5) |> unique() cols <- palette.colors(nlevels(labs)) plot(meuse, axes = T, col = cols, pch = 1) legend("topleft", legend = labs, pch = 1, col = cols) ``` <img src="data:image/png;base64,#mapas_con_R_files/figure-html/unnamed-chunk-17-1.png" width="80%" style="display: block; margin: auto;" /> ] -- .pull-right[ Lo mismo con `sp::spplot()`... ```r spplot(meuse, zcol = "zinc", scales= list(draw =T)) ``` <img src="data:image/png;base64,#mapas_con_R_files/figure-html/unnamed-chunk-18-1.png" width="80%" style="display: block; margin: auto;" /> ] --- template: spplot .pull-left[ Algo mejor... ```r spplot( meuse, zcol = "zinc", scales = list(draw = T), * cuts = seq(0,2000, by = 250), * key.space = "right", ylab = "Northing (m)", xlab = "Easting (m)" ) ``` ] .pull-right[ <img src="data:image/png;base64,#mapas_con_R_files/figure-html/unnamed-chunk-19-1.png" width="504" style="display: block; margin: auto;" /> ] --- template: spplot .pull-left[ Más de una variable... ```r spplot( * meuse, zcol = c("zinc", "lead"), scales = list(draw = T), ylab = "Northing (m)", xlab = "Easting (m)", key.space = "bottom" ) ``` ] .pull-right[ <img src="data:image/png;base64,#mapas_con_R_files/figure-html/unnamed-chunk-20-1.png" width="504" style="display: block; margin: auto;" /> ] --- template: spplot .pull-left[ Ploteando rasters ```r p1 <- spplot( meuse.grid, zcol= "dist", scales = list(draw = T), ylab = "Northing (m)", xlab = "Easting (m)", * at = seq(0, 1, by = 0.05) ) p1 ``` ] .pull-right[ <img src="data:image/png;base64,#mapas_con_R_files/figure-html/unnamed-chunk-21-1.png" width="504" style="display: block; margin: auto;" /> ] --- template: spplot .pull-left[ Ploteando rasters ```r p2 <- spplot( meuse.grid, zcol= "ffreq", scales = list(draw = T), ylab = "Northing (m)", xlab = "Easting (m)" ) p2 ``` ] .pull-right[ <img src="data:image/png;base64,#mapas_con_R_files/figure-html/unnamed-chunk-22-1.png" width="504" style="display: block; margin: auto;" /> ] --- template: spplot Combinando gráficos con `gridExtra::grid.arrange()` ```r grid.arrange(p1, p2, ncol = 2) ``` <img src="data:image/png;base64,#mapas_con_R_files/figure-html/sp6-1.png" width="864" style="display: block; margin: auto;" /> --- name: sfplot background-image: url(data:image/png;base64,#https://user-images.githubusercontent.com/520851/34887433-ce1d130e-f7c6-11e7-83fc-d60ad4fae6bd.gif) background-position: 95% 5% background-size: 7% ## `sf::plot()` --- template: sfplot .pull-left[ - Sólo para vectores (puntos, lineas, poligonos, etc) - Muy rápido, ideal para explorar - Sintaxis similar a `base::plot()`, los elementos se agrega... ```r meuse_sf <- st_as_sf(meuse) plot(meuse_sf) ``` ] .pull-right[ <img src="data:image/png;base64,#mapas_con_R_files/figure-html/unnamed-chunk-23-1.png" width="504" style="display: block; margin: auto;" /> ] --- name: ggsf background-image: url(data:image/png;base64,#https://raw.githubusercontent.com/rstudio/hex-stickers/master/PNG/ggplot2.png) background-position: 95% 5% background-size: 5% ## `ggplot2` + `sf` + `ggspatial` --- template: ggsf .pull-left[ - Vectores (puntos, líneas, polígonos) y rasters - Sigue la lógica de _grammar of graphics_ - `geom_sf()`/`coords_sf()` - `ggspatial` permite agregar mapa base, escalas, etc. ```r ggplot(meuse_sf) + aes(color = zinc) + * geom_sf() + * coord_sf(datum = NULL) ``` ] .pull-right[ <img src="data:image/png;base64,#mapas_con_R_files/figure-html/unnamed-chunk-24-1.png" width="504" style="display: block; margin: auto;" /> ] --- template: ggsf .pull-left[ - Vectores (puntos, lineas, poligonos) y rasters - Sigue la lógica de _grammar of graphics_ - `geom_sf()`/`coords_sf()` - `ggspatial` permite agregar mapa base, escalas, etc. ```r ggplot(meuse_sf) + * annotation_map_tile("osm", zoomin = 0) + aes(color = zinc) + geom_sf() + * coord_sf() ``` ] .pull-right[ <img src="data:image/png;base64,#mapas_con_R_files/figure-html/unnamed-chunk-25-1.png" width="504" style="display: block; margin: auto;" /> ] --- template: ggsf .pull-left[ Podemos usar facets ```r meuse_sf |> pivot_longer(cadmium:zinc) |> ggplot() + * annotation_map_tile("osm", zoomin = 0) + aes(color = value) + geom_sf() + facet_wrap(~ name) ``` ] .pull-right[ <img src="data:image/png;base64,#mapas_con_R_files/figure-html/unnamed-chunk-26-1.png" width="504" style="display: block; margin: auto;" /> ] --- template: ggsf .pull-left[ O `patchwork` para tener una escala por panel... ```r # Graficos individuales p_base <- ggplot(meuse_sf) + * annotation_map_tile("osm", zoomin = 0) + geom_sf() + theme_bw() p_zinc <- p_base + aes(color = zinc) p_lead <- p_base +aes(color = lead) p_cadmium <- p_base + aes(color = cadmium) p_copper <- p_base + aes(color = copper) # Composición (p_zinc + p_cadmium) /(p_copper + p_lead) ``` ] .pull-right[ <img src="data:image/png;base64,#mapas_con_R_files/figure-html/unnamed-chunk-27-1.png" width="504" style="display: block; margin: auto;" /> ] --- name: tmap ## `tmap` --- template: tmap .pull-left[ - Vectores (puntos, líneas, polígonos) y rasters - Sigue la lógica de _grammar of graphics_ - Mapas estáticos y dinámicos (via `leaflet`) - `tm_shape()` es la base que define el set de datos, luego `+ tm_*` agregan capas - El orden de los factorers altera el producto ```r tmap_mode("plot") # modo estático, "view" para dinámico # Poligonos usando Agromet provs <- mapa_provincias() dptos <- mapa_departamentos() mapa_base <- tm_shape(provs) + tm_borders() mapa_base ``` ] .pull-right[ <img src="data:image/png;base64,#mapas_con_R_files/figure-html/unnamed-chunk-28-1.png" width="504" style="display: block; margin: auto;" /> ] --- template: tmap .pull-left[ Agregando los estaciones con `tm_dots()` ```r estaciones <- metadatos_nh() |> st_as_sf(coords = c("lon", "lat")) |> st_set_crs(4326) colores <- c(INTA = "blue", SMN = "red") mapa_est_org <- tm_shape(estaciones) + tm_dots("organismo", palette = colores) + tm_layout(legend.outside = T) mapa_base + mapa_est_org ``` ] .pull-right[ <img src="data:image/png;base64,#mapas_con_R_files/figure-html/unnamed-chunk-29-1.png" width="504" style="display: block; margin: auto;" /> ] --- template: tmap .pull-left[ Un ejemplo con escala numérica... ```r mapa_est_alt <- tm_shape(estaciones) + tm_bubbles("altura") + tm_layout(legend.outside = T) mapa_base + mapa_est_alt ``` ] .pull-right[ <img src="data:image/png;base64,#mapas_con_R_files/figure-html/unnamed-chunk-30-1.png" width="504" style="display: block; margin: auto;" /> ] --- template: tmap .pull-left[ Y ambas a la vez?... ```r mapa_est_alt_org <- tm_shape(estaciones) + tm_bubbles("altura", col = "organismo", palette = colores) + tm_layout(legend.outside = T) mapa_base + mapa_est_alt_org ``` ] .pull-right[ <img src="data:image/png;base64,#mapas_con_R_files/figure-html/unnamed-chunk-31-1.png" width="504" style="display: block; margin: auto;" /> ] --- template: tmap .pull-left[ Podemos usar facets... ```r provincias <- c("Buenos Aires", "Santa Fe", "Córdoba") provs |> filter(name %in% provincias) |> tm_shape() + tm_borders() + * tm_facets("name") + estaciones |> filter(provincia %in% provincias) |> tm_shape() + tm_dots("organismo", size = 0.25, palette = colores) + * tm_facets("provincia", ncol = 1) ``` ] -- .pull-right[ <img src="data:image/png;base64,#mapas_con_R_files/figure-html/unnamed-chunk-32-1.png" width="504" style="display: block; margin: auto;" /> ] --- template: tmap .pull-left[ Para agregar elementos adicionales - `tm_graticule()` para grilla coordenadas, permite reproyectar - `tm_compass()` para rosa de los vientos - `tm_scalebar()` para escala - `tm_layout()` títulos, etc. ```r mapa_base + mapa_est_org + tm_graticules() + tm_compass( type = "arrow", size = 2, position = c("left","top") ) + tm_scale_bar() + tm_layout( main.title = "Estaciones NH", main.title.size = 1 ) ``` ] -- .pull-right[ <img src="data:image/png;base64,#mapas_con_R_files/figure-html/unnamed-chunk-33-1.png" width="504" style="display: block; margin: auto;" /> ] --- template: tmap .pull-left[ También podemos hacer mapas dentro de mapas usando `grid::viewport()` ```r # Mapa principal mapa_bsas <- provs |> filter(name == "Buenos Aires") |> tm_shape() + tm_borders() + estaciones |> filter(provincia == "Buenos Aires") |> tm_shape() + tm_dots("organismo", size = 0.05, palette = colores) + * tm_text("estacion", size = 0.75, ymod = 0.5, remove.overlap = T) + tm_layout(frame = F) # Minimapa provs <- provs |> mutate(foco = ifelse(name == "Buenos Aires", "gray", "white")) mapa_arg <- tm_shape(provs) + tm_polygons("foco") # Imprimir mapa principal y submapa mapa_bsas *vp <- grid::viewport(x = 0.9, y = 0.2, width = 0.3, height = 0.3) *print(mapa_arg, vp = vp) ``` ] -- .pull-right[ <img src="data:image/png;base64,#mapas_con_R_files/figure-html/unnamed-chunk-34-1.png" width="504" style="display: block; margin: auto;" /> ] --- template: tmap .pull-left[ Podemos alternar entre mapas estáticos/dinámicos definiendo el modo `plot` o `view` y usar `tm_view()` para controlar argumentos de `leaflet` ```r tmap_mode("view") mapa_est_org + tm_view( view.legend.position = c("right", "bottom"), set.view = 4 # mas grande mas zoom ) # mas grande mas zoom # tmap_mode("plot") # para volver ``` ] -- .pull-right[
] --- name: mpvw background-image: url(data:image/png;base64,#https://github.com/tim-salabim/mvl/blob/cstriestohelp/imagery/animated/box_anim.gif?raw=true) background-position: 95% 5% background-size: 10% ## `mapview` --- template: mpvw .pull-left[ - Vectores (puntos, líneas, polígonos) y rasters, spacetime - Sólo mapas interactivos - Sintaxis super simple, parecida a spplot - Controles automaticos (activar/desactivar capas, mapa base, zoom, atributos) - Ideal para explorar! ```r mapview(meuse_sf, zcol = "zinc") ``` ] .pull-right[
] --- template: mpvw .pull-left[ - Se pueden poner varias capas a la vez - Controla opacidad, mapa base, etc. ```r mapviewOptions(basemaps = "Esri.WorldImagery") pts <- mapview(meuse_sf, zcol = c("zinc", "lead")) rst <- mapview(meuse.grid, zcol = c("dist"), alpha = 0.5) rst + pts ``` ] .pull-right[
] --- name: leaflet background-image: url(data:image/png;base64,#https://leafletjs.com/docs/images/logo.png) background-position: 95% 10% background-size: 10% ## `leaflet` --- template: leaflet .pull-left[ - Vectores (puntos, lineas, poligonos) `sp` o `sf` - Sigue la lógica de _grammar of graphics_ (capas) - Sólo graficos dinámicos, pensado para webs. - Sintaxis un poco más compleja, control mas fino ```r leaflet(estaciones) |> addCircles() ``` ] .pull-right[
] --- template: leaflet .pull-left[ Podemos agregar capas con `add*()` - `add*Tiles()` para mapas base provedores y servidores WMS - `add*Markers()` para puntos de ubicacion (etiquetas, simbolos, círculos) - `addPolygons()/addPolylines()` para capas de polígonos y lineas - `addCircles()/addRectangles()` geometrias - `addPopups()/addLabels()` etiquetas o callouts ```r leaflet() |> addProviderTiles(provider = "Esri.WorldImagery") |> addCircles(data = estaciones) |> addMarkers(lat = -31.440157, lng = -60.940639, popup = "Acá estoy") |> addPolygons(data = provs, fill = NA, color = "white", weight = 0.5) ``` ] .pull-right[
] --- template: leaflet .pull-left[ - Los marcadores se pueden agrupar según el zoom, - Los elementos se pueden mapear atributos a color, forma, etc. usando paletas: `color*()`, ```r # definir paleta pal <- colorFactor(c("red", "blue"), domain = c("INTA", "SMN")) # Mapa leaflet(estaciones) |> addProviderTiles(provider = "Esri.WorldImagery") |> addCircleMarkers( data = estaciones, fillColor = ~ pal(organismo), color = "transparent", fillOpacity = 1, clusterOptions = markerClusterOptions() # << ) |> # << addMarkers(lat = -31.440157, lng = -60.940639, popup = "Acá estoy") |> addPolygons(data = provs, fill = NA, color = "white", weight = 0.5) |> addLegend(data = estaciones, "bottomright", pal = pal, values = ~ organismo) ``` ] .pull-right[
] --- ## Comentarios finales - Un mapa es la visualizacion por defecto de datos espaciales. -- - En **R** podemos hacer mapas completos de manera reproducible -- - El ecosistema de librerías está cambiando, y rápido. -- - Cada vez más flexibilidad para visualizar datos espaciales. -- --- class: center, middle # Gracias